﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;

namespace WebSite
{
    public class ControleRepresentanteDependencyResolver : IDependencyResolver
    {
        private readonly DependencyResolver m_DependencyResolver;
        private readonly List<IBindHelper> m_BindHelpers;

        public ControleRepresentanteDependencyResolver()
        {
            m_DependencyResolver = new DependencyResolver();
            m_BindHelpers = new List<IBindHelper>();
        }

        public object GetService(Type serviceType)
        {
            if (serviceType.IsSubclassOf(typeof(Controller)))
            {
                ConstructorInfo[] constructorInfos = serviceType.GetConstructors();
                foreach (ConstructorInfo constructorInfo in constructorInfos)
                {
                    ParameterInfo[] parameterInfos = constructorInfo.GetParameters();
                    IEnumerable<Type> types = parameterInfos.Select(x => x.ParameterType);
                    if (types.All(x => m_BindHelpers.Any(y => y.CanHandle(x))))
                    {
                        IEnumerable<object> parameters = types.Select(GetService);
                        return Activator.CreateInstance(serviceType, parameters.ToArray());
                    }
                }
            }
            IBindHelper bindHelper = m_BindHelpers.FirstOrDefault(x => x.CanHandle(serviceType));
            object service = bindHelper != null 
                                 ? bindHelper.GetService()
                                 : m_DependencyResolver.InnerCurrent.GetService(serviceType);
            return service;
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return m_BindHelpers.Where(x => x.CanHandle(serviceType)).Union(
                m_DependencyResolver.InnerCurrent.GetServices(serviceType));
        }

        public BindHelper<T> Bind<T>()
        {
            BindHelper<T> bindHelper = new BindHelper<T>();
            m_BindHelpers.Add(bindHelper);
            return bindHelper;
        }

        private interface IBindHelper
        {
            bool CanHandle(Type serviceType);
            object GetService();
        }

        public class BindHelper<T> : IBindHelper
        {
            private enum BindHelperType
            {
                None,
                Constant,
                Object,
                Method
            }
            private T m_Constant;
            private BindHelperType m_BindHelperType;
            private Type m_Object;
            private Func<T> m_Method;

            public BindHelper()
            {
                m_BindHelperType = BindHelperType.None;
            }

            public void ToConstant(T o)
            {
                m_Constant = o;
                m_BindHelperType = BindHelperType.Constant;
            }

            public void To<TObject>()
            {
                m_Object = typeof (TObject);
                m_BindHelperType = BindHelperType.Object;
            }

            public void ToMethod(Func<T> func)
            {
                m_Method = func;
                m_BindHelperType = BindHelperType.Method;
            }

            public bool CanHandle(Type serviceType)
            {
                return typeof(T).IsSubclassOf(serviceType) || typeof(T).IsAssignableFrom(serviceType);
            }

            public object GetService()
            {
                switch (m_BindHelperType)
                {
                    case BindHelperType.Constant:
                        return m_Constant;
                    case BindHelperType.Method:
                        return m_Method();
                    case BindHelperType.Object:
                        return Activator.CreateInstance(m_Object);
                    default:
                        throw new Exception("Invalid BindHelperType");
                }
            }
        }
    }
}